package tools.similarity;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import opennlp.tools.tokenize.Tokenizer;
import tools.nlp.TokenizerSimple;
import tools.twitter.TweetTidy;

import edu.hit.irlab.nlp.stopwords.StopWords;

public class Cosine {
	/**
	 * Calculate the cosine similarity between param1 and param2. Tread unsafe<br>
	 * The word in the parameters should be already separated by space
	 * 
	 * @param str1
	 *            Tokenized String1
	 * @param str2
	 *            Tokenized String2
	 * @return cosine similarity
	 */
	public static double getCosine(String[] str1, String[] str2) {
		Map<String, Integer> count1 = new HashMap<>();
		Map<String, Integer> count2 = new HashMap<>();
		Map<String, Boolean> allWords = new HashMap<>();

		for (String word : str1) {
			if (count1.containsKey(word)) {
				count1.put(word, count1.get(word) + 1);
			} else {
				count1.put(word, 1);
			}
			allWords.put(word, true);
		}
		for (String word : str2) {
			if (count2.containsKey(word)) {
				count2.put(word, count2.get(word) + 1);
			} else {
				count2.put(word, 1);
			}
			allWords.put(word, true);
		}

		int sum = 0;
		int squareSum1 = 0;
		int squareSum2 = 0;
		for (String word : allWords.keySet()) {
			int c1 = count1.get(word) == null ? 0 : count1.get(word);
			int c2 = count2.get(word) == null ? 0 : count2.get(word);
			sum += c1 * c2;
			squareSum1 += (c1 * c1);
			squareSum2 += (c2 * c2);
		}
		
		if (sum == 0) {
			return 0.0;
		}

		// double cosine = Math.sqrt((sum * sum) / (squareSum1 * squareSum2));
		double cosine = sum / (Math.sqrt(squareSum1 * squareSum2));

		return cosine;
	}

	/**
	 * Calculate the cosine similarity between param1 and param2. Tread unsafe<br>
	 * The word in the parameters should be already separated by space
	 * 
	 * @param str1
	 *            Tokenized String1
	 * @param str2
	 *            Tokenized String2
	 * @param stopwords
	 *            Stop words
	 * @return cosine similarity
	 */
	public static double getCosine(String[] str1, String[] str2, HashMap<String, Boolean> stopwords) {
		Map<String, Integer> count1 = new HashMap<>();
		Map<String, Integer> count2 = new HashMap<>();
		Map<String, Boolean> allWords = new HashMap<>();

		for (String word : str1) {
			if (stopwords.containsKey(word)) {
				continue;
			}
			if (count1.containsKey(word)) {
				count1.put(word, count1.get(word) + 1);
			} else {
				count1.put(word, 1);
			}
			allWords.put(word, true);
		}
		for (String word : str2) {
			if (stopwords.containsKey(word)) {
				continue;
			}
			if (count2.containsKey(word)) {
				count2.put(word, count2.get(word) + 1);
			} else {
				count2.put(word, 1);
			}
			allWords.put(word, true);
		}

		int sum = 0;
		int squareSum1 = 0;
		int squareSum2 = 0;
		for (String word : allWords.keySet()) {
			int c1 = count1.get(word) == null ? 0 : count1.get(word);
			int c2 = count2.get(word) == null ? 0 : count2.get(word);
			sum += c1 * c2;
			squareSum1 += (c1 * c1);
			squareSum2 += (c2 * c2);
		}
		
		if (sum == 0) {
			return 0.0;
		}

		// double cosine = Math.sqrt((sum * sum) / (squareSum1 * squareSum2));
		double cosine = sum / (Math.sqrt(squareSum1 * squareSum2));

		return cosine;
	}
	
	/**
	 * 
	 * @param a
	 * @param b
	 * @param allSen
	 * @return
	 */
	public static double getCosine(int a, int b, ArrayList<Map<String, Integer>> allSen) {
		Map<String, Integer> count1 = allSen.get(a);
		Map<String, Integer> count2 = allSen.get(b);
		Map<String, Boolean> allWords = new HashMap<>();
		int str1Length = 0;
		int str2Length = 0;
		
		for (String word : count1.keySet()) {
			allWords.put(word, true);
			str1Length = str1Length + count1.get(word);
		}
		for (String word : count2.keySet()) {
			allWords.put(word, true);
			str2Length = str2Length + count2.get(word);
		}
		
		int sum = 0;
		int squareSum1 = 0;
		int squareSum2 = 0;
		for (String word : allWords.keySet()) {
			int c1 = count1.get(word) == null ? 0 : count1.get(word);
			int c2 = count2.get(word) == null ? 0 : count2.get(word);
			sum += c1 * c2;
			squareSum1 += (c1 * c1);
			squareSum2 += (c2 * c2);
		}
		
		if (sum == 0) {
			return 0.0;
		}

		double cosine = sum / (Math.sqrt(squareSum1 * squareSum2));

		return cosine;
	}
	
	/**
	 * 
	 * @param a
	 * @param b
	 * @param allSen1
	 * @param allSen2
	 * @return
	 */
	public static double getCosine(int a, int b, ArrayList<Map<String, Integer>> allSen1, ArrayList<Map<String, Integer>> allSen2) {
		Map<String, Integer> count1 = allSen1.get(a);
		Map<String, Integer> count2 = allSen2.get(b);
		Map<String, Boolean> allWords = new HashMap<>();
		int str1Length = 0;
		int str2Length = 0;
		
		for (String word : count1.keySet()) {
			allWords.put(word, true);
			str1Length = str1Length + count1.get(word);
		}
		for (String word : count2.keySet()) {
			allWords.put(word, true);
			str2Length = str2Length + count2.get(word);
		}
		
		int sum = 0;
		int squareSum1 = 0;
		int squareSum2 = 0;
		for (String word : allWords.keySet()) {
			int c1 = count1.get(word) == null ? 0 : count1.get(word);
			int c2 = count2.get(word) == null ? 0 : count2.get(word);
			sum += c1 * c2;
			squareSum1 += (c1 * c1);
			squareSum2 += (c2 * c2);
		}
		
		if (sum == 0) {
			return 0.0;
		}
		
		double cosine = sum / (Math.sqrt(squareSum1 * squareSum2));
		
		return cosine;
	}

	public static void main(String[] args) {
		Vector<String> sw = (new StopWords("data/stopwords.dat")).getStopwords();
		HashMap<String, Boolean> stopwords = new HashMap<>();
		for (String stopword : sw) {
			stopwords.put(stopword, true);
		}
		TokenizerSimple ts = new TokenizerSimple();
		Tokenizer tokenizer = ts.getTokenizer();
		String str1 = TweetTidy.doTidyAll("RT @NewYorkPost: Has anyone seen what the 9/11 Memorial site looks like this evening in New York? #neverforget  http://t.co/SRLe0fp");
		String str2 = TweetTidy.doTidyAll("Has anyone seen what the ground zero site looked like this ev... on Twitpic");
		System.out.println(str1);
		System.out.println(str2);
		System.out.println(Cosine.getCosine(tokenizer.tokenize(str1), tokenizer.tokenize(str2), stopwords));
	}
}
